# TextBufferMarkup.py
# This file is part of labyrinth
#
# Copyright (C) 2007 - Don Scorgie <Don@Scorgie.org>
#
# labyrinth is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# labyrinth is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with labyrinth; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, 
# Boston, MA  02110-1301  USA
#

import gtk
import gobject
import UndoManager
import pango

ADD_ATTR = 42
REMOVE_ATTR = 43

class ExtendedBuffer(gtk.TextBuffer):    
	__gsignals__ = dict (set_focus		= (gobject.SIGNAL_RUN_FIRST,
										   gobject.TYPE_NONE,
										   ()),
						 set_attrs      = (gobject.SIGNAL_RUN_LAST,
						 				   gobject.TYPE_NONE,
						 				   (gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN, pango.FontDescription)))

	def __init__(self, undo_manager, save, save_doc):
		super (gtk.TextBuffer, self).__init__()
		self.undo = undo_manager
		self.connect('insert-text', self.insert_text_cb)
		self.connect_after('insert-text', self.apply_attrs_cb)
		self.connect('delete-range', self.delete_range_cb)
		self.text_elem = save_doc.createTextNode ("Extended")
		self.save = save_doc
		self.element = save
		self.element.appendChild(self.text_elem)
		self.bold_tag = self.create_tag("bold", weight=pango.WEIGHT_BOLD)
		self.italics_tag = self.create_tag("italics", style=pango.STYLE_ITALIC)
		self.underline_tag = self.create_tag("underline", underline=pango.UNDERLINE_SINGLE)
		self.current_tags = []
		self.requested_tags = []
		self.connect_after('mark-set',self.mark_set_cb)
		self.bold_block = False
		self.italic_block = False
		
	
	def undo_action (self, action, mode):
		self.undo.block ()
		self.emit ("set_focus")
		if action.undo_type == UndoManager.DELETE_LETTER or action.undo_type == UndoManager.DELETE_WORD:
			real_mode = not mode
		else:
			real_mode = mode
		if real_mode == UndoManager.UNDO:
			self.delete (self.get_iter_at_offset(action.args[0]),
						 self.get_iter_at_offset (action.args[0]+action.args[2]))
		else:
			self.insert (self.get_iter_at_offset(action.args[0]), action.args[1])
		self.undo.unblock ()
		bold = italics = underline = False
		for x in self.current_tags:
			if x == "bold":
				bold = True
			elif x == "italic":
				italics = True
			elif x == "underline":
				underline = True
		self.emit("set_attrs_bla", bold, italics, underline, None)
		
	def delete_range_cb (self, buffer, iter, it1):
		text = self.get_text (iter, it1)
		self.undo.add_undo (UndoManager.UndoAction (self, UndoManager.DELETE_LETTER, self.undo_action,
													iter.get_offset(), text, len (text), -1, None, None))
		return False
		
	def insert_text_cb (self, buffer, iter, text, length):
		self.undo.add_undo (UndoManager.UndoAction (self, UndoManager.INSERT_LETTER, self.undo_action,
													iter.get_offset(), text, length, None, None))

		return False

	def apply_attrs_cb (self, buffer, iter, text, length):
		prev_iter = iter.copy()
		if not prev_iter.backward_chars(length):
			print "Errored"
		for x in self.current_tags:
			self.apply_tag_by_name(x, prev_iter, iter)
		return False

	def mark_set_cb(self, buffer, iter, mark, *params):
		italics = underline = bold = False
		if iter.has_tag(self.bold_tag):
			bold = True
		elif self.current_tags.count("bold") > 0:
			self.current_tags.remove("bold")
		if iter.has_tag(self.italics_tag):
			italics = True
		elif self.current_tags.count("italics") > 0:
			self.current_tags.remove("italics")
		if iter.has_tag(self.underline_tag):
			underline = True
		elif self.current_tags.count("underline") > 0:
			self.current_tags.remove("underline")
		for x in self.requested_tags:
			if x == "bold":
				bold = True
			if x == "italics":
				italics = True
			if x == "underline":
				underline = True
			if self.current_tags.count(x) == 0:
				self.current_tags.append(x)
		
		self.emit("set_attrs", bold, italics, underline, None)
		return False
		
	def update_save (self):
		next = self.element.firstChild
		while next:
			m = next.nextSibling
			if next.nodeName == "attribute":
				self.element.removeChild (next)
				next.unlink ()
			next = m
			
		self.text_elem.replaceWholeText (self.get_text())
		mark = self.get_insert()
		it = self.get_iter_at_mark(mark)
		self.element.setAttribute("mark", str(it.get_offset())) 
		iter = self.get_start_iter()
		cur = 0
		tags = {}
		doc = self.element.ownerDocument
		tag_table = self.get_tag_table()
		while(1):
			if iter.begins_tag(tag_table.lookup("bold")):
				tags["bold"] = cur
			if iter.ends_tag(tag_table.lookup("bold")):
				elem = doc.createElement ("attribute")
				self.element.appendChild (elem)
				start = tags.pop("bold")
				elem.setAttribute("start", str(start))
				elem.setAttribute("end", str(cur))
				elem.setAttribute("type", "bold")
			if iter.begins_tag(tag_table.lookup("italics")):
				tags["italics"] = cur
			if iter.ends_tag(tag_table.lookup("italics")):
				elem = doc.createElement ("attribute")
				self.element.appendChild (elem)
				start = tags.pop("italics")
				elem.setAttribute("start", str(start))
				elem.setAttribute("end", str(cur))
				elem.setAttribute("type", "italics")
			if iter.begins_tag(tag_table.lookup("underline")):
				tags["underline"] = cur
			if iter.ends_tag(tag_table.lookup("underline")):
				elem = doc.createElement ("attribute")
				self.element.appendChild (elem)
				start = tags.pop("underline")
				elem.setAttribute("start", str(start))
				elem.setAttribute("end", str(cur))
				elem.setAttribute("type", "underline")
			cur+=1
			if not iter.forward_char():
				break
		for x in tags:
			elem = doc.createElement ("attribute")
			self.element.appendChild (elem)
			elem.setAttribute("start", str(tags[x]))
			elem.setAttribute("end", str(-1))
			elem.setAttribute("type", x)

	def load(self, node):
		mark = None
		if node.hasAttribute("mark"):
			mark = int(node.getAttribute("mark"))
		for n in node.childNodes:
			if n.nodeType == n.TEXT_NODE:
				if n.data != "LABYRINTH_AUTOGEN_TEXT_REMOVE":
					self.set_text(n.data)
			elif n.nodeName == "attribute":
				attrType = n.getAttribute("type")
				start = int(n.getAttribute("start"))
				end = int(n.getAttribute("end"))
				start_it = self.get_iter_at_offset(start)
				if end >= 0:
					end_it = self.get_iter_at_offset(end)
				else:
					end_it = self.get_end_iter()

				self.apply_tag_by_name(attrType, start_it, end_it)
			else:
				print "Error: Unknown type: %s.  Ignoring." % n.nodeName
		if mark:
			ins_iter = self.get_iter_at_offset(mark)
			self.move_mark_by_name("insert", ins_iter)
			self.move_mark_by_name("selection_bound", ins_iter)
		
	def get_text (self, start=None, end=None, include_hidden_chars=True):
		if not start: start=self.get_start_iter()
		if not end: end=self.get_end_iter()
		return gtk.TextBuffer.get_text(self,start,end)

	def undo_attr (self, action, mode):
		if mode == UndoManager.UNDO:
			if action.undo_type == ADD_ATTR and len(action.args[1]) > 0:
				self.remove_tag_by_name(action.args[0], action.args[1][0],
										action.args[1][1])
			elif action.undo_type == ADD_ATTR and len(action.args[1]) == 0:			
				self.current_tags.remove(action.args[0])
				self.requested_tags.remove(action.args[0])
			elif action.undo_type == REMOVE_ATTR and len(action.args[1]) > 0:
				self.apply_tag_by_name(action.args[0], action.args[1][0],
									   action.args[1][1])			
			else:
				self.current_tags.append(action.args[0])
				self.requested_tags.append(action.args[0])
		else:
			if action.undo_type == ADD_ATTR and len(action.args[1]) > 0:
				self.apply_tag_by_name(action.args[0], action.args[1][0],
									   action.args[1][1])			
			elif action.undo_type == ADD_ATTR and len(action.args[1]) == 0:	
				self.current_tags.append(action.args[0])
				self.requested_tags.append(action.args[0])
			elif action.undo_type == REMOVE_ATTR and len(action.args[1]) > 0:
				self.remove_tag_by_name(action.args[0], action.args[1][0],
										action.args[1][1])
			else:		
				self.current_tags.remove(action.args[0])
				self.requested_tags.remove(action.args[0])
		bold = italics = underline = False
		for x in self.current_tags:
			if x == "bold":
				bold = True
			elif x == "italics":
				italics = True
			elif x == "underline":
				underline = True
		self.emit("set_attrs", bold, italics, underline)
		
	def	set_bold (self, bold):
		selection = self.get_selection_bounds()
		if bold:
			if len(selection) > 0:
				self.apply_tag_by_name("bold", selection[0], selection[1])
			else:
				self.current_tags.append("bold")
				self.requested_tags.append("bold")
			self.undo.add_undo(UndoManager.UndoAction(self, ADD_ATTR, self.undo_attr,
													  "bold", selection))
		else:
			if len(selection) > 0:
				self.remove_tag_by_name("bold", selection[0], selection[1])	
			else:
				self.current_tags.remove("bold")
				self.requested_tags.remove("bold")
			self.undo.add_undo(UndoManager.UndoAction(self, REMOVE_ATTR, self.undo_attr,
													  "bold", selection))
		
	def	set_italics (self, italics):
		selection = self.get_selection_bounds()
		if italics:
			if len(selection) > 0:
				self.apply_tag_by_name("italics", selection[0], selection[1])
			else:
				self.current_tags.append("italics")
				self.requested_tags.append("italics")
			self.undo.add_undo(UndoManager.UndoAction(self, ADD_ATTR, self.undo_attr,
													  "italics", selection))
		else:
			if len(selection) > 0:
				self.remove_tag_by_name("italics", selection[0], selection[1])	
			else:
				self.current_tags.remove("italics")
				self.requested_tags.remove("italics")
			self.undo.add_undo(UndoManager.UndoAction(self, REMOVE_ATTR, self.undo_attr,
													  "italics", selection))
		
	def	set_underline (self, underline):
		selection = self.get_selection_bounds()
		if underline:
			if len(selection) > 0:
				self.apply_tag_by_name("underline", selection[0], selection[1])
			else:
				self.current_tags.append("underline")
				self.requested_tags.append("underline")
			self.undo.add_undo(UndoManager.UndoAction(self, ADD_ATTR, self.undo_attr,
													  "underline", selection))
		else:
			if len(selection) > 0:
				self.remove_tag_by_name("underline", selection[0], selection[1])	
			else:
				self.current_tags.remove("underline")
				self.requested_tags.remove("underline")
			self.undo.add_undo(UndoManager.UndoAction(self, REMOVE_ATTR, self.undo_attr,
													  "underline", selection))
				
		
		
		
		
		
